import sys, logging, event, socket, struct

readBufSize = 4096


def identToAddr(ident):
    [ip, port] = ident.split(':')
    return (ip, int(port))


def addrToIdent(addr):
    return '%s:%d' % addr


peers = {}

class Channel():
    def __init__(self):
        self.rbuf = ''
        self.wbuf = ''
        self.readEvt = None
        self.writeEvt = None

    def initAccept(self, sock):
        self.sock = sock
        self.ident = None
        self.state = 'handshake'
        self.mark = 2
        self.readEvt = event.read(self.sock.fileno(), self.onRead)


    def initConnect(self, sock, ident):
        self.sock = sock
        self.ident = ident
        self.state = 'head'
        self.mark = 2
        self.readEvt = event.read(self.sock.fileno(), self.onRead)
        
        self.send(struct.pack('H', listenPort))
        addr = identToAddr(ident)
        try:
            self.sock.connect(addr)
        except socket.error, e:
            if e.args[0] != 10035:
                raise
        peers[ident] = self


    def onRead(self):
        self.readEvt = None
        try:
            data = self.sock.recv(readBufSize)
        except socket.error, e:
            if e.args[0] == 10035:
                self.readEvt = event.read(self.sock.fileno(), self.onRead)
            else:
                self.close()
            return

        if not data:
            self.close()
            return

        self.rbuf += data
        while len(self.rbuf) >= self.mark:
            if self.state == 'head':
                self.mark = struct.unpack('H', self.rbuf[:2])[0]
                self.rbuf = self.rbuf[2:]
                if self.mark:
                    self.state = 'body'
                else:
                    self.state = 'head'
                    self.mark = 2
            elif self.state == 'body':
                try:
                    packetHook(self.rbuf[:self.mark], self.ident)
                except:
                    pass
                self.state = 'head'
                self.rbuf = self.rbuf[self.mark:]
                self.mark = 2
            elif self.state == 'handshake':
                addr = (self.sock.getpeername()[0],
                        struct.unpack('H', self.rbuf[:2])[0])
                self.ident = addrToIdent(addr)
                curCh = peers.setdefault(self.ident, self)
                if curCh is not self:
                    curCh.close()
                    peers[self.ident] = self
                self.state = 'head'
                self.mark = 2
                self.rbuf = self.rbuf[2:]
        if running:
            self.readEvt = event.read(self.sock.fileno(), self.onRead)
        else:
            finalize()


    def onWrite(self):
        self.writeEvt = None
        try:
            size = self.sock.send(self.wbuf)
            self.wbuf = self.wbuf[size:]
        except socket.error, e:
            if e.args[0] != 10035:
                self.close()
                return
        if self.wbuf:
            self.writeEvt = event.write(self.sock.fileno(), self.onWrite)


    def send(self, data):
        self.wbuf += data
        if self.wbuf and not self.writeEvt:
            self.writeEvt = event.write(self.sock.fileno(), self.onWrite)


    def close(self):
        self.sock.close()
        if self.ident:
            peers.pop(self.ident, None)
        if self.readEvt:
            self.readEvt.delete()
        if self.writeEvt:
            self.writeEvt.delete()

    #def __del__(self):
    #    print '__del__', self.ident
        

def onAccept():
    sock, addr = listenSock.accept()
    sock.setblocking(False)
    ch = Channel()
    ch.initAccept(sock)

    global acceptEvt
    acceptEvt = event.read(listenSock.fileno(), onAccept)
        

def connect(ident):
    try:
        return peers[ident]
    except KeyError:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.setblocking(False)

        ch = Channel()
        ch.initConnect(sock, ident)
        return ch


def init(hook, port, backlog = 10):
    event.init()
    global packetHook, listenPort, listenSock, acceptEvt
    packetHook = hook
    listenPort = port

    listenSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    listenSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
    listenSock.bind(('', port))
    listenSock.listen(backlog)
    listenSock.setblocking(False)

    acceptEvt = event.read(listenSock, onAccept)


def send(msg, ident):
    ch = connect(ident)
    ch.send(struct.pack('H', len(msg)) + msg)


def onTimer(msg):
    try:
        packetHook(msg, 'timer')
    except:
        pass
    if not running:
        finalize()

def timer(msg, timeout):
    event.timeout(timeout, onTimer, msg)


def start():
    global running
    running = True
    event.dispatch()


def stop():
    global running
    running = False


def finalize():
    event.abort()
    listenSock.close()
    acceptEvt.delete()
    for c in peers.values():
        c.close()
    peers.clear()
